1   /*
2    * Javassist, a Java-bytecode translator toolkit.
3    * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
4    *
5    * The contents of this file are subject to the Mozilla Public License Version
6    * 1.1 (the "License"); you may not use this file except in compliance with
7    * the License.  Alternatively, the contents of this file may be used under
8    * the terms of the GNU Lesser General Public License Version 2.1 or later,
9    * or the Apache License Version 2.0.
10   *
11   * Software distributed under the License is distributed on an "AS IS" basis,
12   * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13   * for the specific language governing rights and limitations under the
14   * License.
15   */
16  
17  package scouter.javassist;
18  
19  import scouter.javassist.CannotCompileException;
20  import scouter.javassist.ClassMap;
21  import scouter.javassist.ClassPool;
22  import scouter.javassist.CodeConverter;
23  import scouter.javassist.CtClass;
24  import scouter.javassist.CtConstructor;
25  import scouter.javassist.CtField;
26  import scouter.javassist.CtMember;
27  import scouter.javassist.CtPrimitiveType;
28  import scouter.javassist.Modifier;
29  import scouter.javassist.NotFoundException;
30  import scouter.javassist.bytecode.AccessFlag;
31  import scouter.javassist.bytecode.AnnotationsAttribute;
32  import scouter.javassist.bytecode.AttributeInfo;
33  import scouter.javassist.bytecode.BadBytecode;
34  import scouter.javassist.bytecode.Bytecode;
35  import scouter.javassist.bytecode.CodeAttribute;
36  import scouter.javassist.bytecode.CodeIterator;
37  import scouter.javassist.bytecode.ConstPool;
38  import scouter.javassist.bytecode.Descriptor;
39  import scouter.javassist.bytecode.ExceptionsAttribute;
40  import scouter.javassist.bytecode.LineNumberAttribute;
41  import scouter.javassist.bytecode.LocalVariableAttribute;
42  import scouter.javassist.bytecode.LocalVariableTypeAttribute;
43  import scouter.javassist.bytecode.MethodInfo;
44  import scouter.javassist.bytecode.Opcode;
45  import scouter.javassist.bytecode.ParameterAnnotationsAttribute;
46  import scouter.javassist.bytecode.SignatureAttribute;
47  import scouter.javassist.bytecode.StackMap;
48  import scouter.javassist.bytecode.StackMapTable;
49  import scouter.javassist.compiler.CompileError;
50  import scouter.javassist.compiler.Javac;
51  import scouter.javassist.expr.ExprEditor;
52  
53  /**
54   * <code>CtBehavior</code> represents a method, a constructor,
55   * or a static constructor (class initializer). 
56   * It is the abstract super class of
57   * <code>CtMethod</code> and <code>CtConstructor</code>.
58   *
59   * <p>To directly read or modify bytecode, obtain <code>MethodInfo</code>
60   * objects.
61   *
62   * @see #getMethodInfo()
63   */
64  public abstract class CtBehavior extends CtMember {
65      protected MethodInfo methodInfo;
66  
67      protected CtBehavior(CtClass clazz, MethodInfo minfo) {
68          super(clazz);
69          methodInfo = minfo;
70      }
71  
72      /**
73       * @param isCons        true if this is a constructor.
74       */
75      void copy(CtBehavior src, boolean isCons, ClassMap map)
76          throws CannotCompileException
77      {
78          CtClass declaring = declaringClass;
79          MethodInfo srcInfo = src.methodInfo;
80          CtClass srcClass = src.getDeclaringClass();
81          ConstPool cp = declaring.getClassFile2().getConstPool();
82  
83          map = new ClassMap(map);
84          map.put(srcClass.getName(), declaring.getName());
85          try {
86              boolean patch = false;
87              CtClass srcSuper = srcClass.getSuperclass();
88              CtClass destSuper = declaring.getSuperclass();
89              String destSuperName = null;
90              if (srcSuper != null && destSuper != null) {
91                  String srcSuperName = srcSuper.getName();
92                  destSuperName = destSuper.getName();
93                  if (!srcSuperName.equals(destSuperName))
94                      if (srcSuperName.equals(CtClass.javaLangObject))
95                          patch = true;
96                      else
97                          map.putIfNone(srcSuperName, destSuperName);
98              }
99  
100             // a stack map table is copied from srcInfo.
101             methodInfo = new MethodInfo(cp, srcInfo.getName(), srcInfo, map);
102             if (isCons && patch)
103                 methodInfo.setSuperclass(destSuperName);
104         }
105         catch (NotFoundException e) {
106             throw new CannotCompileException(e);
107         }
108         catch (BadBytecode e) {
109             throw new CannotCompileException(e);
110         }
111     }
112 
113     protected void extendToString(StringBuffer buffer) {
114         buffer.append(' ');
115         buffer.append(getName());
116         buffer.append(' ');
117         buffer.append(methodInfo.getDescriptor());
118     }
119 
120     /**
121      * Returns the method or constructor name followed by parameter types
122      * such as <code>javassist.CtBehavior.stBody(String)</code>.
123      *
124      * @since 3.5
125      */
126     public abstract String getLongName();
127 
128     /**
129      * Returns the <code>MethodInfo</code> representing this method/constructor in the
130      * class file.
131      *
132      * <p>If you modify the bytecode through the returned
133      * <code>MethodInfo</code> object, you might have to explicitly
134      * rebuild a stack map table.  Javassist does not automatically
135      * rebuild it for avoiding unnecessary rebuilding.
136      *
137      * @see scouter.javassist.bytecode.MethodInfo#rebuildStackMap(ClassPool)
138      */
139     public MethodInfo getMethodInfo() {
140         declaringClass.checkModify();
141         return methodInfo;
142     }
143 
144     /**
145      * Returns the <code>MethodInfo</code> representing the method/constructor in the
146      * class file (read only).
147      * Normal applications do not need calling this method.  Use
148      * <code>getMethodInfo()</code>.
149      *
150      * <p>The <code>MethodInfo</code> object obtained by this method
151      * is read only.  Changes to this object might not be reflected
152      * on a class file generated by <code>toBytecode()</code>,
153      * <code>toClass()</code>, etc in <code>CtClass</code>.
154      *
155      * <p>This method is available even if the <code>CtClass</code>
156      * containing this method is frozen.  However, if the class is
157      * frozen, the <code>MethodInfo</code> might be also pruned.
158      *
159      * @see #getMethodInfo()
160      * @see CtClass#isFrozen()
161      * @see CtClass#prune()
162      */
163     public MethodInfo getMethodInfo2() { return methodInfo; }
164 
165     /**
166      * Obtains the modifiers of the method/constructor.
167      *
168      * @return          modifiers encoded with
169      *                  <code>javassist.Modifier</code>.
170      * @see Modifier
171      */
172     public int getModifiers() {
173         return AccessFlag.toModifier(methodInfo.getAccessFlags());
174     }
175 
176     /**
177      * Sets the encoded modifiers of the method/constructor.
178      *
179      * <p>Changing the modifiers may cause a problem.
180      * For example, if a non-static method is changed to static,
181      * the method will be rejected by the bytecode verifier.
182      *
183      * @see Modifier
184      */
185     public void setModifiers(int mod) {
186         declaringClass.checkModify();
187         methodInfo.setAccessFlags(AccessFlag.of(mod));
188     }
189 
190     /**
191      * Returns true if the class has the specified annotation type.
192      *
193      * @param typeName      the name of annotation type.
194      * @return <code>true</code> if the annotation is found,
195      *         otherwise <code>false</code>.
196      * @since 3.21
197      */
198     public boolean hasAnnotation(String typeName) {
199        MethodInfo mi = getMethodInfo2();
200        AnnotationsAttribute ainfo = (AnnotationsAttribute)
201                    mi.getAttribute(AnnotationsAttribute.invisibleTag);  
202        AnnotationsAttribute ainfo2 = (AnnotationsAttribute)
203                    mi.getAttribute(AnnotationsAttribute.visibleTag);  
204        return CtClassType.hasAnnotationType(typeName,
205                                             getDeclaringClass().getClassPool(),
206                                             ainfo, ainfo2);
207     }
208 
209     /**
210      * Returns the annotation if the class has the specified annotation class.
211      * For example, if an annotation <code>@Author</code> is associated
212      * with this method/constructor, an <code>Author</code> object is returned.
213      * The member values can be obtained by calling methods on
214      * the <code>Author</code> object.
215      *
216      * @param clz the annotation class.
217      * @return the annotation if found, otherwise <code>null</code>.
218      * @since 3.11
219      */
220     public Object getAnnotation(Class clz) throws ClassNotFoundException {
221        MethodInfo mi = getMethodInfo2();
222        AnnotationsAttribute ainfo = (AnnotationsAttribute)
223                    mi.getAttribute(AnnotationsAttribute.invisibleTag);  
224        AnnotationsAttribute ainfo2 = (AnnotationsAttribute)
225                    mi.getAttribute(AnnotationsAttribute.visibleTag);  
226        return CtClassType.getAnnotationType(clz,
227                                             getDeclaringClass().getClassPool(),
228                                             ainfo, ainfo2);
229     }
230 
231     /**
232      * Returns the annotations associated with this method or constructor.
233      *
234      * @return an array of annotation-type objects.
235      * @see #getAvailableAnnotations()
236      * @since 3.1
237      */
238     public Object[] getAnnotations() throws ClassNotFoundException {
239        return getAnnotations(false);
240    }
241 
242     /**
243      * Returns the annotations associated with this method or constructor.
244      * If any annotations are not on the classpath, they are not included
245      * in the returned array.
246      * 
247      * @return an array of annotation-type objects.
248      * @see #getAnnotations()
249      * @since 3.3
250      */
251     public Object[] getAvailableAnnotations(){
252        try{
253            return getAnnotations(true);
254        }
255        catch (ClassNotFoundException e){
256            throw new RuntimeException("Unexpected exception", e);
257        }
258     }
259 
260     private Object[] getAnnotations(boolean ignoreNotFound)
261        throws ClassNotFoundException
262     {
263        MethodInfo mi = getMethodInfo2();
264        AnnotationsAttribute ainfo = (AnnotationsAttribute)
265                    mi.getAttribute(AnnotationsAttribute.invisibleTag);  
266        AnnotationsAttribute ainfo2 = (AnnotationsAttribute)
267                    mi.getAttribute(AnnotationsAttribute.visibleTag);  
268        return CtClassType.toAnnotationType(ignoreNotFound,
269                                            getDeclaringClass().getClassPool(),
270                                            ainfo, ainfo2);
271     }
272 
273     /**
274      * Returns the parameter annotations associated with this method or constructor.
275      *
276      * @return an array of annotation-type objects.  The length of the returned array is
277      * equal to the number of the formal parameters.  If each parameter has no
278      * annotation, the elements of the returned array are empty arrays.
279      *
280      * @see #getAvailableParameterAnnotations()
281      * @see #getAnnotations()
282      * @since 3.1
283      */
284     public Object[][] getParameterAnnotations() throws ClassNotFoundException {
285         return getParameterAnnotations(false);
286     }
287 
288     /**
289      * Returns the parameter annotations associated with this method or constructor.
290      * If any annotations are not on the classpath, they are not included in the
291      * returned array.
292      * 
293      * @return an array of annotation-type objects.  The length of the returned array is
294      * equal to the number of the formal parameters.  If each parameter has no
295      * annotation, the elements of the returned array are empty arrays.
296      *
297      * @see #getParameterAnnotations()
298      * @see #getAvailableAnnotations()
299      * @since 3.3
300      */
301     public Object[][] getAvailableParameterAnnotations(){
302         try {
303             return getParameterAnnotations(true);
304         }
305         catch(ClassNotFoundException e) {
306             throw new RuntimeException("Unexpected exception", e);
307         }
308     }
309 
310     Object[][] getParameterAnnotations(boolean ignoreNotFound)
311         throws ClassNotFoundException
312     {
313         MethodInfo mi = getMethodInfo2();
314         ParameterAnnotationsAttribute ainfo = (ParameterAnnotationsAttribute)
315                     mi.getAttribute(ParameterAnnotationsAttribute.invisibleTag);  
316         ParameterAnnotationsAttribute ainfo2 = (ParameterAnnotationsAttribute)
317                     mi.getAttribute(ParameterAnnotationsAttribute.visibleTag);  
318         return CtClassType.toAnnotationType(ignoreNotFound,
319                                             getDeclaringClass().getClassPool(),
320                                             ainfo, ainfo2, mi);
321     }
322 
323     /**
324      * Obtains parameter types of this method/constructor.
325      */
326     public CtClass[] getParameterTypes() throws NotFoundException {
327         return Descriptor.getParameterTypes(methodInfo.getDescriptor(),
328                                             declaringClass.getClassPool());
329     }
330 
331     /**
332      * Obtains the type of the returned value.
333      */
334     CtClass getReturnType0() throws NotFoundException {
335         return Descriptor.getReturnType(methodInfo.getDescriptor(),
336                                         declaringClass.getClassPool());
337     }
338 
339     /**
340      * Returns the method signature (the parameter types
341      * and the return type).
342      * The method signature is represented by a character string
343      * called method descriptor, which is defined in the JVM specification.
344      * If two methods/constructors have
345      * the same parameter types
346      * and the return type, <code>getSignature()</code> returns the
347      * same string (the return type of constructors is <code>void</code>).
348      *
349      * <p>Note that the returned string is not the type signature
350      * contained in the <code>SignatureAttirbute</code>.  It is
351      * a descriptor.
352      *
353      * @see scouter.javassist.bytecode.Descriptor
354      * @see #getGenericSignature()
355      */
356     public String getSignature() {
357         return methodInfo.getDescriptor();
358     }
359 
360     /**
361      * Returns the generic signature of the method.
362      * It represents parameter types including type variables.
363      *
364      * @see SignatureAttribute#toMethodSignature(String)
365      * @since 3.17
366      */
367     public String getGenericSignature() {
368         SignatureAttribute sa
369             = (SignatureAttribute)methodInfo.getAttribute(SignatureAttribute.tag);
370         return sa == null ? null : sa.getSignature();
371     }
372 
373     /**
374      * Set the generic signature of the method.
375      * It represents parameter types including type variables.
376      * See {@link scouter.javassist.CtClass#setGenericSignature(String)}
377      * for a code sample.
378      *
379      * @param sig       a new generic signature.
380      * @see scouter.javassist.bytecode.SignatureAttribute.MethodSignature#encode()
381      * @since 3.17
382      */
383     public void setGenericSignature(String sig) {
384         declaringClass.checkModify();
385         methodInfo.addAttribute(new SignatureAttribute(methodInfo.getConstPool(), sig));
386     }
387 
388     /**
389      * Obtains exceptions that this method/constructor may throw.
390      *
391      * @return a zero-length array if there is no throws clause.
392      */
393     public CtClass[] getExceptionTypes() throws NotFoundException {
394         String[] exceptions;
395         ExceptionsAttribute ea = methodInfo.getExceptionsAttribute();
396         if (ea == null)
397             exceptions = null;
398         else
399             exceptions = ea.getExceptions();
400 
401         return declaringClass.getClassPool().get(exceptions);
402     }
403 
404     /**
405      * Sets exceptions that this method/constructor may throw.
406      */
407     public void setExceptionTypes(CtClass[] types) throws NotFoundException {
408         declaringClass.checkModify();
409         if (types == null || types.length == 0) {
410             methodInfo.removeExceptionsAttribute();
411             return;
412         }
413 
414         String[] names = new String[types.length];
415         for (int i = 0; i < types.length; ++i)
416             names[i] = types[i].getName();
417 
418         ExceptionsAttribute ea = methodInfo.getExceptionsAttribute();
419         if (ea == null) {
420             ea = new ExceptionsAttribute(methodInfo.getConstPool());
421             methodInfo.setExceptionsAttribute(ea);
422         }
423 
424         ea.setExceptions(names);
425     }
426 
427     /**
428      * Returns true if the body is empty.
429      */
430     public abstract boolean isEmpty();
431 
432     /**
433      * Sets a method/constructor body.
434      *
435      * @param src       the source code representing the body.
436      *                  It must be a single statement or block.
437      *                  If it is <code>null</code>, the substituted
438      *                  body does nothing except returning zero or null.
439      */
440     public void setBody(String src) throws CannotCompileException {
441         setBody(src, null, null);
442     }
443 
444     /**
445      * Sets a method/constructor body.
446      *
447      * @param src       the source code representing the body.
448      *                  It must be a single statement or block.
449      *                  If it is <code>null</code>, the substituted
450      *                  body does nothing except returning zero or null.
451      * @param delegateObj       the source text specifying the object
452      *                          that is called on by <code>$proceed()</code>.
453      * @param delegateMethod    the name of the method
454      *                          that is called by <code>$proceed()</code>.
455      */
456     public void setBody(String src,
457                         String delegateObj, String delegateMethod)
458         throws CannotCompileException
459     {
460         CtClass cc = declaringClass;
461         cc.checkModify();
462         try {
463             Javac jv = new Javac(cc);
464             if (delegateMethod != null)
465                 jv.recordProceed(delegateObj, delegateMethod);
466 
467             Bytecode b = jv.compileBody(this, src);
468             methodInfo.setCodeAttribute(b.toCodeAttribute());
469             methodInfo.setAccessFlags(methodInfo.getAccessFlags()
470                                       & ~AccessFlag.ABSTRACT);
471             methodInfo.rebuildStackMapIf6(cc.getClassPool(), cc.getClassFile2());
472             declaringClass.rebuildClassFile();
473         }
474         catch (CompileError e) {
475             throw new CannotCompileException(e);
476         } catch (BadBytecode e) {
477             throw new CannotCompileException(e);
478         }
479     }
480 
481     static void setBody0(CtClass srcClass, MethodInfo srcInfo,
482                          CtClass destClass, MethodInfo destInfo,
483                          ClassMap map)
484         throws CannotCompileException
485     {
486         destClass.checkModify();
487 
488         map = new ClassMap(map);
489         map.put(srcClass.getName(), destClass.getName());
490         try {
491             CodeAttribute cattr = srcInfo.getCodeAttribute();
492             if (cattr != null) {
493                 ConstPool cp = destInfo.getConstPool();
494                 CodeAttribute ca = (CodeAttribute)cattr.copy(cp, map);
495                 destInfo.setCodeAttribute(ca);
496                 // a stack map table is copied to destInfo.
497             }
498         }
499         catch (CodeAttribute.RuntimeCopyException e) {
500             /* the exception may be thrown by copy() in CodeAttribute.
501              */
502             throw new CannotCompileException(e);
503         }
504 
505         destInfo.setAccessFlags(destInfo.getAccessFlags()
506                                 & ~AccessFlag.ABSTRACT);
507         destClass.rebuildClassFile();
508     }
509 
510     /**
511      * Obtains an attribute with the given name.
512      * If that attribute is not found in the class file, this
513      * method returns null.
514      *
515      * <p>Note that an attribute is a data block specified by
516      * the class file format.  It is not an annotation.
517      * See {@link scouter.javassist.bytecode.AttributeInfo}.
518      *
519      * @param name              attribute name
520      */
521     public byte[] getAttribute(String name) {
522         AttributeInfo ai = methodInfo.getAttribute(name);
523         if (ai == null)
524             return null;
525         else
526             return ai.get();
527     }
528 
529     /**
530      * Adds an attribute. The attribute is saved in the class file.
531      *
532      * <p>Note that an attribute is a data block specified by
533      * the class file format.  It is not an annotation.
534      * See {@link scouter.javassist.bytecode.AttributeInfo}.
535      *
536      * @param name      attribute name
537      * @param data      attribute value
538      */
539     public void setAttribute(String name, byte[] data) {
540         declaringClass.checkModify();
541         methodInfo.addAttribute(new AttributeInfo(methodInfo.getConstPool(),
542                                                   name, data));
543     }
544 
545     /**
546      * Declares to use <code>$cflow</code> for this method/constructor.
547      * If <code>$cflow</code> is used, the class files modified
548      * with Javassist requires a support class
549      * <code>javassist.runtime.Cflow</code> at runtime
550      * (other Javassist classes are not required at runtime).
551      *
552      * <p>Every <code>$cflow</code> variable is given a unique name.
553      * For example, if the given name is <code>"Point.paint"</code>,
554      * then the variable is indicated by <code>$cflow(Point.paint)</code>.
555      *
556      * @param name      <code>$cflow</code> name.  It can include
557      *                  alphabets, numbers, <code>_</code>,
558      *                  <code>$</code>, and <code>.</code> (dot).
559      *
560      * @see scouter.javassist.runtime.Cflow
561      */
562     public void useCflow(String name) throws CannotCompileException {
563         CtClass cc = declaringClass;
564         cc.checkModify();
565         ClassPool pool = cc.getClassPool();
566         String fname;
567         int i = 0;
568         while (true) {
569             fname = "_cflow$" + i++;
570             try {
571                 cc.getDeclaredField(fname);
572             }
573             catch(NotFoundException e) {
574                 break;
575             }
576         }
577 
578         pool.recordCflow(name, declaringClass.getName(), fname);
579         try {
580             CtClass type = pool.get("javassist.runtime.Cflow");
581             CtField field = new CtField(type, fname, cc);
582             field.setModifiers(Modifier.PUBLIC | Modifier.STATIC);
583             cc.addField(field, CtField.Initializer.byNew(type));
584             insertBefore(fname + ".enter();", false);
585             String src = fname + ".exit();";
586             insertAfter(src, true);
587         }
588         catch (NotFoundException e) {
589             throw new CannotCompileException(e);
590         }
591     }
592 
593     /**
594      * Declares a new local variable.  The scope of this variable is the
595      * whole method body.  The initial value of that variable is not set.
596      * The declared variable can be accessed in the code snippet inserted
597      * by <code>insertBefore()</code>, <code>insertAfter()</code>, etc.
598      *
599      * <p>If the second parameter <code>asFinally</code> to
600      * <code>insertAfter()</code> is true, the declared local variable
601      * is not visible from the code inserted by <code>insertAfter()</code>.
602      *
603      * @param name      the name of the variable
604      * @param type      the type of the variable
605      * @see #insertBefore(String)
606      * @see #insertAfter(String)
607      */
608     public void addLocalVariable(String name, CtClass type)
609         throws CannotCompileException
610     {
611         declaringClass.checkModify();
612         ConstPool cp = methodInfo.getConstPool();
613         CodeAttribute ca = methodInfo.getCodeAttribute();
614         if (ca == null)
615             throw new CannotCompileException("no method body");
616 
617         LocalVariableAttribute va = (LocalVariableAttribute)ca.getAttribute(
618                                                 LocalVariableAttribute.tag);
619         if (va == null) {
620             va = new LocalVariableAttribute(cp);
621             ca.getAttributes().add(va);
622         }
623 
624         int maxLocals = ca.getMaxLocals();
625         String desc = Descriptor.of(type);
626         va.addEntry(0, ca.getCodeLength(),
627                     cp.addUtf8Info(name), cp.addUtf8Info(desc), maxLocals);
628         ca.setMaxLocals(maxLocals + Descriptor.dataSize(desc));
629     }
630 
631     /**
632      * Inserts a new parameter, which becomes the first parameter.
633      */
634     public void insertParameter(CtClass type)
635         throws CannotCompileException
636     {
637         declaringClass.checkModify();
638         String desc = methodInfo.getDescriptor();
639         String desc2 = Descriptor.insertParameter(type, desc);
640         try {
641             addParameter2(Modifier.isStatic(getModifiers()) ? 0 : 1, type, desc);
642         }
643         catch (BadBytecode e) {
644             throw new CannotCompileException(e);
645         }
646 
647         methodInfo.setDescriptor(desc2);
648     }
649 
650     /**
651      * Appends a new parameter, which becomes the last parameter.
652      */
653     public void addParameter(CtClass type)
654         throws CannotCompileException
655     {
656         declaringClass.checkModify();
657         String desc = methodInfo.getDescriptor();
658         String desc2 = Descriptor.appendParameter(type, desc);
659         int offset = Modifier.isStatic(getModifiers()) ? 0 : 1;
660         try {
661             addParameter2(offset + Descriptor.paramSize(desc), type, desc);
662         }
663         catch (BadBytecode e) {
664             throw new CannotCompileException(e);
665         }
666 
667         methodInfo.setDescriptor(desc2);
668     }
669 
670     private void addParameter2(int where, CtClass type, String desc)
671         throws BadBytecode
672     {
673         CodeAttribute ca = methodInfo.getCodeAttribute();
674         if (ca != null) {
675             int size = 1;
676             char typeDesc = 'L';
677             int classInfo = 0;
678             if (type.isPrimitive()) {
679                 CtPrimitiveType cpt = (CtPrimitiveType)type;
680                 size = cpt.getDataSize();
681                 typeDesc = cpt.getDescriptor();
682             }
683             else
684                 classInfo = methodInfo.getConstPool().addClassInfo(type);
685 
686             ca.insertLocalVar(where, size);
687             LocalVariableAttribute va
688                 = (LocalVariableAttribute)ca.getAttribute(LocalVariableAttribute.tag);
689             if (va != null)
690                 va.shiftIndex(where, size);
691 
692             LocalVariableTypeAttribute lvta
693                 = (LocalVariableTypeAttribute)ca.getAttribute(LocalVariableTypeAttribute.tag);
694             if (lvta != null)
695                 lvta.shiftIndex(where, size);
696 
697             StackMapTable smt = (StackMapTable)ca.getAttribute(StackMapTable.tag);
698             if (smt != null)
699                 smt.insertLocal(where, StackMapTable.typeTagOf(typeDesc), classInfo);
700 
701             StackMap sm = (StackMap)ca.getAttribute(StackMap.tag);
702             if (sm != null)
703                 sm.insertLocal(where, StackMapTable.typeTagOf(typeDesc), classInfo);
704         }
705     }
706 
707     /**
708      * Modifies the method/constructor body.
709      *
710      * @param converter         specifies how to modify.
711      */
712     public void instrument(CodeConverter converter)
713         throws CannotCompileException
714     {
715         declaringClass.checkModify();
716         ConstPool cp = methodInfo.getConstPool();
717         converter.doit(getDeclaringClass(), methodInfo, cp);
718     }
719 
720     /**
721      * Modifies the method/constructor body.
722      *
723      * <p>While executing this method, only <code>replace()</code>
724      * in <code>Expr</code> is available for bytecode modification.
725      * Other methods such as <code>insertBefore()</code> may collapse
726      * the bytecode because the <code>ExprEditor</code> loses
727      * its current position.  
728      *
729      * @param editor            specifies how to modify.
730      * @see scouter.javassist.expr.Expr#replace(String)
731      * @see #insertBefore(String)
732      */
733     public void instrument(ExprEditor editor)
734         throws CannotCompileException
735     {
736         // if the class is not frozen,
737         // does not turn the modified flag on.
738         if (declaringClass.isFrozen())
739             declaringClass.checkModify();
740 
741         if (editor.doit(declaringClass, methodInfo))
742             declaringClass.checkModify();
743     }
744 
745     /**
746      * Inserts bytecode at the beginning of the body.
747      *
748      * <p>If this object represents a constructor,
749      * the bytecode is inserted before
750      * a constructor in the super class or this class is called.
751      * Therefore, the inserted bytecode is subject to constraints described
752      * in Section 4.8.2 of The Java Virtual Machine Specification (2nd ed).
753      * For example, it cannot access instance fields or methods although
754      * it may assign a value to an instance field directly declared in this
755      * class.  Accessing static fields and methods is allowed.
756      * Use <code>insertBeforeBody()</code> in <code>CtConstructor</code>.
757      *
758      * @param src       the source code representing the inserted bytecode.
759      *                  It must be a single statement or block.
760      * @see CtConstructor#insertBeforeBody(String)
761      */
762     public void insertBefore(String src) throws CannotCompileException {
763         insertBefore(src, true);
764     }
765 
766     private void insertBefore(String src, boolean rebuild)
767         throws CannotCompileException
768     {
769         CtClass cc = declaringClass;
770         cc.checkModify();
771         CodeAttribute ca = methodInfo.getCodeAttribute();
772         if (ca == null)
773             throw new CannotCompileException("no method body");
774 
775         CodeIterator iterator = ca.iterator();
776         Javac jv = new Javac(cc);
777         try {
778             int nvars = jv.recordParams(getParameterTypes(),
779                                         Modifier.isStatic(getModifiers()));
780             jv.recordParamNames(ca, nvars);
781             jv.recordLocalVariables(ca, 0);
782             jv.recordType(getReturnType0());
783             jv.compileStmnt(src);
784             Bytecode b = jv.getBytecode();
785             int stack = b.getMaxStack();
786             int locals = b.getMaxLocals();
787 
788             if (stack > ca.getMaxStack())
789                 ca.setMaxStack(stack);
790 
791             if (locals > ca.getMaxLocals())
792                 ca.setMaxLocals(locals);
793 
794             int pos = iterator.insertEx(b.get());
795             iterator.insert(b.getExceptionTable(), pos);
796             if (rebuild)
797                 methodInfo.rebuildStackMapIf6(cc.getClassPool(), cc.getClassFile2());
798         }
799         catch (NotFoundException e) {
800             throw new CannotCompileException(e);
801         }
802         catch (CompileError e) {
803             throw new CannotCompileException(e);
804         }
805         catch (BadBytecode e) {
806             throw new CannotCompileException(e);
807         }
808     }
809 
810     /**
811      * Inserts bytecode at the end of the body.
812      * The bytecode is inserted just before every return insturction.
813      * It is not executed when an exception is thrown.
814      *
815      * @param src       the source code representing the inserted bytecode.
816      *                  It must be a single statement or block.
817      */
818     public void insertAfter(String src)
819         throws CannotCompileException
820     {
821         insertAfter(src, false);
822     }
823 
824     /**
825      * Inserts bytecode at the end of the body.
826      * The bytecode is inserted just before every return insturction.
827      *
828      * @param src       the source code representing the inserted bytecode.
829      *                  It must be a single statement or block.
830      * @param asFinally         true if the inserted bytecode is executed
831      *                  not only when the control normally returns
832      *                  but also when an exception is thrown.
833      *                  If this parameter is true, the inserted code cannot
834      *                  access local variables.
835      */
836     public void insertAfter(String src, boolean asFinally)
837         throws CannotCompileException
838     {
839         CtClass cc = declaringClass;
840         cc.checkModify();
841         ConstPool pool = methodInfo.getConstPool();
842         CodeAttribute ca = methodInfo.getCodeAttribute();
843         if (ca == null)
844             throw new CannotCompileException("no method body");
845 
846         CodeIterator iterator = ca.iterator();
847         int retAddr = ca.getMaxLocals();
848         Bytecode b = new Bytecode(pool, 0, retAddr + 1);
849         b.setStackDepth(ca.getMaxStack() + 1);
850         Javac jv = new Javac(b, cc);
851         try {
852             int nvars = jv.recordParams(getParameterTypes(),
853                                         Modifier.isStatic(getModifiers()));
854             jv.recordParamNames(ca, nvars);
855             CtClass rtype = getReturnType0();
856             int varNo = jv.recordReturnType(rtype, true);
857             jv.recordLocalVariables(ca, 0);
858 
859             // finally clause for exceptions
860             int handlerLen = insertAfterHandler(asFinally, b, rtype, varNo,
861                                                 jv, src);
862             int handlerPos = iterator.getCodeLength();
863             if (asFinally)
864                 ca.getExceptionTable().add(getStartPosOfBody(ca), handlerPos, handlerPos, 0); 
865 
866             int adviceLen = 0;
867             int advicePos = 0;
868             boolean noReturn = true;
869             while (iterator.hasNext()) {
870                 int pos = iterator.next();
871                 if (pos >= handlerPos)
872                     break;
873 
874                 int c = iterator.byteAt(pos);
875                 if (c == Opcode.ARETURN || c == Opcode.IRETURN
876                     || c == Opcode.FRETURN || c == Opcode.LRETURN
877                     || c == Opcode.DRETURN || c == Opcode.RETURN) {
878                     if (noReturn) {
879                         // finally clause for normal termination
880                         adviceLen = insertAfterAdvice(b, jv, src, pool, rtype, varNo);
881                         handlerPos = iterator.append(b.get());
882                         iterator.append(b.getExceptionTable(), handlerPos);
883                         advicePos = iterator.getCodeLength() - adviceLen;
884                         handlerLen = advicePos - handlerPos;
885                         noReturn = false;
886                     }
887                     insertGoto(iterator, advicePos, pos);
888                     advicePos = iterator.getCodeLength() - adviceLen;
889                     handlerPos = advicePos - handlerLen;
890                 }
891             }
892 
893             if (noReturn) {
894                 handlerPos = iterator.append(b.get());
895                 iterator.append(b.getExceptionTable(), handlerPos);
896             }
897 
898             ca.setMaxStack(b.getMaxStack());
899             ca.setMaxLocals(b.getMaxLocals());
900             methodInfo.rebuildStackMapIf6(cc.getClassPool(), cc.getClassFile2());
901         }
902         catch (NotFoundException e) {
903             throw new CannotCompileException(e);
904         }
905         catch (CompileError e) {
906             throw new CannotCompileException(e);
907         }
908         catch (BadBytecode e) {
909             throw new CannotCompileException(e);
910         }
911     }
912 
913     private int insertAfterAdvice(Bytecode code, Javac jv, String src,
914                                   ConstPool cp, CtClass rtype, int varNo)
915         throws CompileError
916     {
917         int pc = code.currentPc();
918         if (rtype == CtClass.voidType) {
919             code.addOpcode(Opcode.ACONST_NULL);
920             code.addAstore(varNo);
921             jv.compileStmnt(src);
922             code.addOpcode(Opcode.RETURN);
923             if (code.getMaxLocals() < 1)
924                 code.setMaxLocals(1);
925         }
926         else {
927             code.addStore(varNo, rtype);
928             jv.compileStmnt(src);
929             code.addLoad(varNo, rtype);
930             if (rtype.isPrimitive())
931                 code.addOpcode(((CtPrimitiveType)rtype).getReturnOp());
932             else
933                 code.addOpcode(Opcode.ARETURN);
934         }
935 
936         return code.currentPc() - pc;
937     }
938 
939     /*
940      * assert subr > pos
941      */
942     private void insertGoto(CodeIterator iterator, int subr, int pos)
943         throws BadBytecode
944     {
945         iterator.setMark(subr);
946         // the gap length might be a multiple of 4.
947         iterator.writeByte(Opcode.NOP, pos);
948         boolean wide = subr + 2 - pos > Short.MAX_VALUE;
949         int len = wide ? 4 : 2;
950         CodeIterator.Gap gap = iterator.insertGapAt(pos, len, false);
951         pos = gap.position + gap.length - len;
952         int offset = iterator.getMark() - pos;
953         if (wide) {
954             iterator.writeByte(Opcode.GOTO_W, pos);
955             iterator.write32bit(offset, pos + 1);
956         }
957         else if (offset <= Short.MAX_VALUE) {
958             iterator.writeByte(Opcode.GOTO, pos);
959             iterator.write16bit(offset, pos + 1);
960         }
961         else {
962             if (gap.length < 4) {
963                 CodeIterator.Gap gap2 =  iterator.insertGapAt(gap.position, 2, false);
964                 pos = gap2.position + gap2.length + gap.length - 4; 
965             }
966 
967             iterator.writeByte(Opcode.GOTO_W, pos);
968             iterator.write32bit(iterator.getMark() - pos, pos + 1);
969         }
970     }
971 
972     /* insert a finally clause
973      */
974     private int insertAfterHandler(boolean asFinally, Bytecode b,
975                                    CtClass rtype, int returnVarNo,
976                                    Javac javac, String src)
977         throws CompileError
978     {
979         if (!asFinally)
980             return 0;
981 
982         int var = b.getMaxLocals();
983         b.incMaxLocals(1);
984         int pc = b.currentPc();
985         b.addAstore(var);   // store an exception
986         if (rtype.isPrimitive()) {
987             char c = ((CtPrimitiveType)rtype).getDescriptor();
988             if (c == 'D') {
989                 b.addDconst(0.0);
990                 b.addDstore(returnVarNo);
991             }
992             else if (c == 'F') {
993                 b.addFconst(0);
994                 b.addFstore(returnVarNo);
995             }
996             else if (c == 'J') {
997                 b.addLconst(0);
998                 b.addLstore(returnVarNo);
999             }
1000             else if (c == 'V') {
1001                 b.addOpcode(Opcode.ACONST_NULL);
1002                 b.addAstore(returnVarNo);
1003             }
1004             else { // int, boolean, char, short, ...
1005                 b.addIconst(0);
1006                 b.addIstore(returnVarNo);
1007             }
1008         }
1009         else {
1010             b.addOpcode(Opcode.ACONST_NULL);
1011             b.addAstore(returnVarNo);
1012         }
1013 
1014         javac.compileStmnt(src);
1015         b.addAload(var);
1016         b.addOpcode(Opcode.ATHROW);
1017         return b.currentPc() - pc;
1018     }
1019 
1020     /* -- OLD version --
1021 
1022     public void insertAfter(String src) throws CannotCompileException {
1023         declaringClass.checkModify();
1024         CodeAttribute ca = methodInfo.getCodeAttribute();
1025         CodeIterator iterator = ca.iterator();
1026         Bytecode b = new Bytecode(methodInfo.getConstPool(),
1027                                   ca.getMaxStack(), ca.getMaxLocals());
1028         b.setStackDepth(ca.getMaxStack());
1029         Javac jv = new Javac(b, declaringClass);
1030         try {
1031             jv.recordParams(getParameterTypes(),
1032                             Modifier.isStatic(getModifiers()));
1033             CtClass rtype = getReturnType0();
1034             int varNo = jv.recordReturnType(rtype, true);
1035             boolean isVoid = rtype == CtClass.voidType;
1036             if (isVoid) {
1037                 b.addOpcode(Opcode.ACONST_NULL);
1038                 b.addAstore(varNo);
1039                 jv.compileStmnt(src);
1040             }
1041             else {
1042                 b.addStore(varNo, rtype);
1043                 jv.compileStmnt(src);
1044                 b.addLoad(varNo, rtype);
1045             }
1046 
1047             byte[] code = b.get();
1048             ca.setMaxStack(b.getMaxStack());
1049             ca.setMaxLocals(b.getMaxLocals());
1050             while (iterator.hasNext()) {
1051                 int pos = iterator.next();
1052                 int c = iterator.byteAt(pos);
1053                 if (c == Opcode.ARETURN || c == Opcode.IRETURN
1054                     || c == Opcode.FRETURN || c == Opcode.LRETURN
1055                     || c == Opcode.DRETURN || c == Opcode.RETURN)
1056                     iterator.insert(pos, code);
1057             }
1058         }
1059         catch (NotFoundException e) {
1060             throw new CannotCompileException(e);
1061         }
1062         catch (CompileError e) {
1063             throw new CannotCompileException(e);
1064         }
1065         catch (BadBytecode e) {
1066             throw new CannotCompileException(e);
1067         }
1068     }
1069     */
1070 
1071     /**
1072      * Adds a catch clause that handles an exception thrown in the
1073      * body.  The catch clause must end with a return or throw statement.
1074      *
1075      * @param src       the source code representing the catch clause.
1076      *                  It must be a single statement or block.
1077      * @param exceptionType     the type of the exception handled by the
1078      *                          catch clause.
1079      */
1080     public void addCatch(String src, CtClass exceptionType)
1081         throws CannotCompileException
1082     {
1083         addCatch(src, exceptionType, "$e");
1084     }
1085 
1086     /**
1087      * Adds a catch clause that handles an exception thrown in the
1088      * body.  The catch clause must end with a return or throw statement.
1089      *
1090      * @param src       the source code representing the catch clause.
1091      *                  It must be a single statement or block.
1092      * @param exceptionType     the type of the exception handled by the
1093      *                          catch clause.
1094      * @param exceptionName     the name of the variable containing the
1095      *                          caught exception, for example,
1096      *                          <code>$e</code>.
1097      */
1098     public void addCatch(String src, CtClass exceptionType,
1099                          String exceptionName)
1100         throws CannotCompileException
1101     {
1102         CtClass cc = declaringClass;
1103         cc.checkModify();
1104         ConstPool cp = methodInfo.getConstPool();
1105         CodeAttribute ca = methodInfo.getCodeAttribute();
1106         CodeIterator iterator = ca.iterator();
1107         Bytecode b = new Bytecode(cp, ca.getMaxStack(), ca.getMaxLocals());
1108         b.setStackDepth(1);
1109         Javac jv = new Javac(b, cc);
1110         try {
1111             jv.recordParams(getParameterTypes(),
1112                             Modifier.isStatic(getModifiers()));
1113             int var = jv.recordVariable(exceptionType, exceptionName);
1114             b.addAstore(var);
1115             jv.compileStmnt(src);
1116 
1117             int stack = b.getMaxStack();
1118             int locals = b.getMaxLocals();
1119 
1120             if (stack > ca.getMaxStack())
1121                 ca.setMaxStack(stack);
1122 
1123             if (locals > ca.getMaxLocals())
1124                 ca.setMaxLocals(locals);
1125 
1126             int len = iterator.getCodeLength();
1127             int pos = iterator.append(b.get());
1128             ca.getExceptionTable().add(getStartPosOfBody(ca), len, len,
1129                                        cp.addClassInfo(exceptionType));
1130             iterator.append(b.getExceptionTable(), pos);
1131             methodInfo.rebuildStackMapIf6(cc.getClassPool(), cc.getClassFile2());
1132         }
1133         catch (NotFoundException e) {
1134             throw new CannotCompileException(e);
1135         }
1136         catch (CompileError e) {
1137             throw new CannotCompileException(e);
1138         } catch (BadBytecode e) {
1139             throw new CannotCompileException(e);
1140         }
1141     }
1142 
1143     /* CtConstructor overrides this method.
1144      */
1145     int getStartPosOfBody(CodeAttribute ca) throws CannotCompileException {
1146         return 0;
1147     }
1148 
1149     /**
1150      * Inserts bytecode at the specified line in the body.
1151      * It is equivalent to:
1152      *
1153      * <br><code>insertAt(lineNum, true, src)</code>
1154      *
1155      * <br>See this method as well.
1156      *
1157      * @param lineNum   the line number.  The bytecode is inserted at the
1158      *                  beginning of the code at the line specified by this
1159      *                  line number.
1160      * @param src       the source code representing the inserted bytecode.
1161      *                  It must be a single statement or block.
1162      * @return      the line number at which the bytecode has been inserted.
1163      *
1164      * @see CtBehavior#insertAt(int,boolean,String)
1165      */
1166     public int insertAt(int lineNum, String src)
1167         throws CannotCompileException
1168     {
1169         return insertAt(lineNum, true, src);
1170     }
1171 
1172     /**
1173      * Inserts bytecode at the specified line in the body.
1174      *
1175      * <p>If there is not
1176      * a statement at the specified line, the bytecode might be inserted
1177      * at the line including the first statement after that line specified.
1178      * For example, if there is only a closing brace at that line, the
1179      * bytecode would be inserted at another line below.
1180      * To know exactly where the bytecode will be inserted, call with
1181      * <code>modify</code> set to <code>false</code>. 
1182      *
1183      * @param lineNum   the line number.  The bytecode is inserted at the
1184      *                  beginning of the code at the line specified by this
1185      *                  line number.
1186      * @param modify    if false, this method does not insert the bytecode.
1187      *                  It instead only returns the line number at which
1188      *                  the bytecode would be inserted.
1189      * @param src       the source code representing the inserted bytecode.
1190      *                  It must be a single statement or block.
1191      *                  If modify is false, the value of src can be null.
1192      * @return      the line number at which the bytecode has been inserted.
1193      */
1194     public int insertAt(int lineNum, boolean modify, String src)
1195         throws CannotCompileException
1196     {
1197         CodeAttribute ca = methodInfo.getCodeAttribute();
1198         if (ca == null)
1199             throw new CannotCompileException("no method body");
1200 
1201         LineNumberAttribute ainfo
1202             = (LineNumberAttribute)ca.getAttribute(LineNumberAttribute.tag);
1203         if (ainfo == null)
1204             throw new CannotCompileException("no line number info");
1205 
1206         LineNumberAttribute.Pc pc = ainfo.toNearPc(lineNum);
1207         lineNum = pc.line;
1208         int index = pc.index;
1209         if (!modify)
1210             return lineNum;
1211 
1212         CtClass cc = declaringClass;
1213         cc.checkModify();
1214         CodeIterator iterator = ca.iterator();
1215         Javac jv = new Javac(cc);
1216         try {
1217             jv.recordLocalVariables(ca, index);
1218             jv.recordParams(getParameterTypes(),
1219                             Modifier.isStatic(getModifiers()));
1220             jv.setMaxLocals(ca.getMaxLocals());
1221             jv.compileStmnt(src);
1222             Bytecode b = jv.getBytecode();
1223             int locals = b.getMaxLocals();
1224             int stack = b.getMaxStack();
1225             ca.setMaxLocals(locals);
1226 
1227             /* We assume that there is no values in the operand stack
1228              * at the position where the bytecode is inserted.
1229              */
1230             if (stack > ca.getMaxStack())
1231                 ca.setMaxStack(stack);
1232 
1233             index = iterator.insertAt(index, b.get());
1234             iterator.insert(b.getExceptionTable(), index);
1235             methodInfo.rebuildStackMapIf6(cc.getClassPool(), cc.getClassFile2());
1236             return lineNum;
1237         }
1238         catch (NotFoundException e) {
1239             throw new CannotCompileException(e);
1240         }
1241         catch (CompileError e) {
1242             throw new CannotCompileException(e);
1243         }
1244         catch (BadBytecode e) {
1245             throw new CannotCompileException(e);
1246         }
1247     }
1248 }